package com.gitee.magic.core.utils.codec;

import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SignatureException;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;

import com.gitee.magic.core.exception.ApplicationException;

/**
 * @author start
 */
public class Rsa {

	private static final String INSTANCENAME="RSA";
	public static final String WITHMD5 = "MD5withRSA";
	public static final String WITHSHA1 = "SHA1WithRSA";
	
	/**
	 * 生成密钥对
	 * @param keySize
	 * @return
	 */
    public static KeyPair genKeyPair(int keySize){  
        KeyPairGenerator keyPairGenerator;
		try {
			keyPairGenerator = KeyPairGenerator.getInstance(INSTANCENAME);
		} catch (NoSuchAlgorithmException e) {
			throw new ApplicationException(e);
		}  
        keyPairGenerator.initialize(keySize);        
        return keyPairGenerator.generateKeyPair();  
    }
    
    /**
     * 公钥字符Base64编码
     * @param keyPair
     * @return
     */
	public static String getPublicKey(KeyPair keyPair) {
		return Base64.encode(keyPair.getPublic().getEncoded());
	}
	
	/**
	 * 公钥
	 * @param key
	 * @return
	 */
	public static RSAPublicKey getPublicKey(String key) {
		X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64.decode(key));
		KeyFactory keyFactory;
		try {
			keyFactory = KeyFactory.getInstance(INSTANCENAME);
			return (RSAPublicKey)keyFactory.generatePublic(keySpec);
		} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
			throw new ApplicationException(e);
		}
	}

	/**
	 * 私钥字符Base64编码
	 * @param keyPair
	 * @return
	 */
	public static String getPrivateKey(KeyPair keyPair) {
		return Base64.encode(keyPair.getPrivate().getEncoded());
	}

	/**
	 * 私钥
	 * @param key
	 * @return
	 */
	public static RSAPrivateKey getPrivateKey(String key) {
		PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(Base64.decode(key));
		KeyFactory keyFactory;
		try {
			keyFactory = KeyFactory.getInstance(INSTANCENAME);
			return (RSAPrivateKey)keyFactory.generatePrivate(keySpec);
		} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
			throw new ApplicationException(e);
		}
	}

	 /**
	  * 公钥加密  
	  * @param data
	  * @param publicKey
	  * @return
	  */
    public static String encrypt(String data, String publicKey){  
    	//java默认"RSA"="RSA/ECB/PKCS1Padding"  
        Cipher cipher;
		try {
			cipher = Cipher.getInstance(INSTANCENAME);
	        cipher.init(Cipher.ENCRYPT_MODE, getPublicKey(publicKey));  
	        return Base64.encode(cipher.doFinal(data.getBytes())); 
		} catch (NoSuchAlgorithmException | 
				NoSuchPaddingException | 
				InvalidKeyException | 
				IllegalBlockSizeException | 
				BadPaddingException e) {
			throw new ApplicationException(e);
		} 
    }  
      
    /**
     * 私钥解密
     * @param content
     * @param privateKey
     * @return
     */
    public static String decrypt(String content, String privateKey) {  
        Cipher cipher;
		try {
			cipher = Cipher.getInstance(INSTANCENAME);
	        cipher.init(Cipher.DECRYPT_MODE, getPrivateKey(privateKey));  
	        return new String(cipher.doFinal(Base64.decode(content))); 
		} catch (NoSuchAlgorithmException | 
				NoSuchPaddingException | 
				InvalidKeyException | 
				IllegalBlockSizeException | 
				BadPaddingException e) {
			throw new ApplicationException(e);
		}   
    }  
    
    /**
	 * RSA签名
	 * @param data
	 * @param privateKey
	 * @param algorithms
	 * @return
	 */
	public static String sign(String data, String privateKey,String algorithms) {
		PKCS8EncodedKeySpec priPkcs8 = new PKCS8EncodedKeySpec(Base64.decode(privateKey));
		try {
			KeyFactory keyf = KeyFactory.getInstance(INSTANCENAME);
			PrivateKey priKey = keyf.generatePrivate(priPkcs8);
			java.security.Signature signature = java.security.Signature.getInstance(algorithms);
			signature.initSign(priKey);
			signature.update(data.getBytes());
			return Base64.encode(signature.sign());
		} catch (NoSuchAlgorithmException | SignatureException | InvalidKeySpecException | InvalidKeyException e) {
			throw new ApplicationException(e);
		}
	}

	/**
	 * RSA验签名检查
	 * @param content
	 * @param sign
	 * @param publicKey
	 * @param algorithms
	 * @return
	 */
	public static boolean verify(String data, String sign, String publicKey,String algorithms)  {
		KeyFactory keyFactory;
		try {
			keyFactory = KeyFactory.getInstance(INSTANCENAME);
			PublicKey pubKey = keyFactory.generatePublic(new X509EncodedKeySpec(Base64.decode(publicKey)));
			java.security.Signature signature = java.security.Signature.getInstance(algorithms);
			signature.initVerify(pubKey);
			signature.update(data.getBytes());
			return signature.verify(Base64.decode(sign));
		} catch (NoSuchAlgorithmException | 
				InvalidKeySpecException | 
				InvalidKeyException | 
				SignatureException e) {
			throw new ApplicationException(e);
		}
	}

    public static void main(String[] args) throws Exception {
    	KeyPair keyPair=Rsa.genKeyPair(512); 
		//获取公钥
        String publickey=Rsa.getPublicKey(keyPair);
        System.out.println("公钥:"+publickey);  
        //获取私钥
        String privateKey=Rsa.getPrivateKey(keyPair);
        System.out.println("私钥:"+privateKey);  
        
        String data="GnKEEToZwe0sw3yAGnKEEToZwe0sw3yA";
        
        //公钥加密
        String encryptedBytes=Rsa.encrypt(data, publickey);    
        System.out.println("加密:"+encryptedBytes);  
          
        //私钥解密
        String decryptedBytes=Rsa.decrypt(encryptedBytes, privateKey);        
        System.out.println("解密:"+decryptedBytes); 
        
        String sign=Rsa.sign(data, privateKey, WITHSHA1);
        System.out.println("签名:"+sign);
        
        Boolean f=Rsa.verify(data, sign, publickey, WITHSHA1);
        System.out.println("验证:"+f);
	}

}